package com.grantheath.erebus.model 
{
	import com.grantheath.erebus.model.enum.notifications.ErebusInitializationNotifications;
	import com.grantheath.erebus.model.interfaces.IErebusLayersProxy;
	import com.grantheath.erebus.model.interfaces.IErebusAssetLoaderClassReferenceProxy;
	import com.grantheath.erebus.model.interfaces.IErebusAssetNode;
	import com.grantheath.erebus.model.enum.names.ErebusProxyNames;
	import com.grantheath.erebus.model.interfaces.IErebusClassReferenceProxy;
	import com.grantheath.erebus.model.interfaces.IErebusPageNode;
	import com.grantheath.erebus.model.interfaces.IErebusSiteTreeProxy;
	
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.getDefinitionByName;
	import flash.utils.Dictionary;
	import flash.events.IOErrorEvent;
	import flash.display.DisplayObjectContainer;

	/**
	 * @author grantheath
	 */
	public class ErebusXMLSiteTreeProxy extends ErebusSiteTreeProxy implements IErebusSiteTreeProxy 
	{
		private var _classReferenceProxy : IErebusClassReferenceProxy;
		
		protected var xmlSrc : String;

		public function ErebusXMLSiteTreeProxy()
		{
			var siteXMLLoader : URLLoader = new URLLoader();
			
			super();
			if(!xmlSrc){
				xmlSrc = "Site.xml";
			}
			_classReferenceProxy = facade.retrieveProxy(ErebusProxyNames.CLASS_REFERENCE_PROXY) as IErebusClassReferenceProxy;
			siteXMLLoader.addEventListener(Event.COMPLETE, onXMLLoad);
			siteXMLLoader.addEventListener(IOErrorEvent.IO_ERROR, onXMLLoadError);
			siteXMLLoader.load(new URLRequest(xmlSrc));
		}

		private function siteTreeFromXML(siteXML : XML) : IErebusPageNode
		{
			var dataNode : XML = siteXML.page[0];
			var root : IErebusPageNode;
			var childList : XMLList;
			var children : Array = new Array();
			var newChildren : Array;
			var newChild : Dictionary;
			var childDataNode : XML;
			
			//setup tree root
			root = initializePageNodeFromXML(dataNode);
			childList = dataNode.page;
			for each(childDataNode in childList) {
				newChild = new Dictionary();
				newChild["data"] = childDataNode;
				newChild["parent"] = root;
				children.push(newChild);
			}
			//build site tree
			while(children.length > 0) {
				newChildren = new Array();
				for each(var nodeDictionary : Dictionary in children) {
					var newPageNode : IErebusPageNode;
					
					childDataNode = nodeDictionary["data"] as XML;
					newPageNode = initializePageNodeFromXML(childDataNode, nodeDictionary["parent"] as IErebusPageNode);
					childList = childDataNode.page;
					for each(childDataNode in childList) {
						newChild = new Dictionary();
						newChild["data"] = childDataNode;
						newChild["parent"] = newPageNode;
						newChildren.push(newChild);
					}
				}
				children = newChildren;
			}
			return root;
		}
		
		private function initializePageNodeFromXML(dataNode : XML, parent : IErebusPageNode = null) : IErebusPageNode
		{
			//TODO make erebus class something more abstract set it like you can with preloader classes
			var pageLoaderClass : Class = _classReferenceProxy.pageLoaderClasses[0] as Class;
			var preloaderClass : Class =  _classReferenceProxy.pagePreloaderClasses[0] as Class;
			var pageNodeClass : Class = _classReferenceProxy.pageNodeClasses[0] as Class;
			var transitionHandelerClass : Class = _classReferenceProxy.tranisitionHandelerClasses[0] as Class;
			var preloaderTransitionhandelerClass : Class = _classReferenceProxy.tranisitionHandelerClasses[0] as Class;
			var pageNode : IErebusPageNode = new pageNodeClass();
			var layerProxy : IErebusLayersProxy = facade.retrieveProxy(ErebusProxyNames.LAYERS_PROXY) as IErebusLayersProxy;
			
			//make sure default pageLoader class is defined
			if(!pageLoaderClass){
				//TODO better null pageLoader error
				throw new Error("You have not defined the page loaders for your project");
			}
			//make sure default preloader class is defined
			if(!preloaderClass) {
				//TODO better null proloader error
				throw new Error("You have not defined the preloaders for your project");
			}
			//make sure tree node class is defined
			if(!pageNodeClass){
				//TODO better null tree node error
				throw new Error("You have not defined the pageNode class for your project");
			}
			//define tree node class
			if(String(dataNode.@nodeType)){
				try {
					pageNodeClass = getDefinitionByName(String(dataNode.@pageNode)) as Class;
				} catch(error : Error){
					//TODO better undefined pageNodeClass error
					throw new Error("undefined pageNode class: " + String(dataNode.@pageNode));
				}
			}
			//define page id
			if(String(dataNode.@id)){
				pageNode.pageId = dataNode.@id;
			} else {
				//TODO better id attribute error
				throw new Error("You need to have an id attribute in your stie xml!!");
			}
			//define page src
			if(String(dataNode.@src)) {
				pageNode.pageSrc = dataNode.@src;
			} else {
				//TODO better src attribute error
				throw new Error("you need to have a src attribute in your site xml!!!");
			}
			//define page loader
			if(String(dataNode.@pageLoader)){
				try {
					pageLoaderClass = getDefinitionByName(String(dataNode.@pageLoaderClass)) as Class;
				} catch(error : Error){
					//TODO better undefined page loader error
					throw new Error("undefined page loader class: " + String(dataNode.@pageLoader));
				}
			}
			pageNode.pageLoaderClass = pageLoaderClass;
			//define preloader
			if(String(dataNode.@preloader)){
				try {
					preloaderClass = getDefinitionByName(String(dataNode.@preloader)) as Class;
				} catch(error : Error) {
					//TODO better undefined preloader error
					throw new Error("undefined preloader class: " + String(dataNode.@preloader));
				}
			}
			pageNode.pagePreloaderClass = preloaderClass;
			//define page title
			if(String(dataNode.@title)){
				pageNode.pageTitle = String(dataNode.@title);
			} else {
				pageNode.pageTitle = String(dataNode.@id);
			}
			//define node layer
			if(String(dataNode.@layer)){
				var layer : DisplayObjectContainer = layerProxy.getLayerById(dataNode.@layer);
				
				if(!layer){
					//TODO better undefined layer error
					throw new Error("undefined layer " + String(dataNode.@layer));
				}
				pageNode.pageLayer = layer;
			} else {
				if(layerProxy.defaultLayer) {
					pageNode.pageLayer = layerProxy.defaultLayer;
				} else {
					//TODO better undefined layer error
					throw new Error("layer not defined for node: " + pageNode);
				}
			}
			//define preloader layer
			if(String(dataNode.@preloaderDefault)) {
				var preloaderLayer : DisplayObjectContainer = layerProxy.getLayerById(dataNode.@preloaderDefault);
				
				if(!preloaderLayer){
					//TODO better undefined layer error
					throw new Error("undefined preloader layer " + String(dataNode.@layer));
				}
				pageNode.preloaderLayer = preloaderLayer;
			} else {
				if(layerProxy.defaultPreloaderLayer) {
					pageNode.preloaderLayer = layerProxy.defaultPreloaderLayer;
				} else {
					//TODO better undefined layer error
					throw new Error("preloader layer not defined for node: " + pageNode);
				}
			}
			//define animation handeler
			if(String(dataNode.@animationHandeler)){
				try {
					pageNode.transitionHandelerClass = getDefinitionByName(String(dataNode.@animationHandeler)) as Class;
				} catch(error : Error){
					//TODO better undefined animation handeler error
					throw new Error("undefined animation handeler class: " + String(dataNode.@animationHandeler));
				}
			}
			pageNode.transitionHandelerClass = transitionHandelerClass;
			if(String(dataNode.@preloaderAnimationHandeler)){
				try {
					pageNode.preloaderTransitionHandelerClass = getDefinitionByName(String(dataNode.@animationHandeler)) as Class;
				} catch(error : Error){
					//TODO better undefined animation handeler error
					throw new Error("undefined animation handeler class: " + String(dataNode.@animationHandeler));
				}
			}
			pageNode.preloaderTransitionHandelerClass =  preloaderTransitionhandelerClass;
			//define page assets
			if(dataNode.asset){
				var assetList : XMLList = dataNode.asset;
				
				if(assetList.length() > 0){
					trace(dataNode.@id + " assets:");
					for each(var assetDataNode : XML in assetList){
						trace("\tasset: " + assetDataNode.@id);
						pageNode.pageAssets.push(initializeAssetNodeFromXML(assetDataNode, pageNode));
					}
				}
			}
			//define parent/child relationship, treePath, and defaultChild
			if(parent) {
				pageNode.parent = parent;
				parent.addChildPageNode(pageNode);
				if(parent.treePath == "/") {
					pageNode.treePath = "/" + pageNode.pageId;
				} else {
					pageNode.treePath = parent.treePath + "/" + pageNode.pageId;
				}
				if(dataNode.@default == true) {
					parent.defaultChild = pageNode;
				}
			} else {
				pageNode.treePath = "/";
			}
			return pageNode;
		}
		
		private function initializeAssetNodeFromXML(dataNode : XML, pageNode : IErebusPageNode) : IErebusAssetNode
		{
			var assetLoaderClassReference : IErebusAssetLoaderClassReferenceProxy = facade.retrieveProxy(ErebusProxyNames.ASSET_LOADER_CLASS_REFERENCE_PROXY) as IErebusAssetLoaderClassReferenceProxy;
			var assetNodeClass : Class = _classReferenceProxy.assetNodeClasses[0] as Class;
			var assetLoaderClass : Class;
			var assetNode : IErebusAssetNode;
			
			if(!assetNodeClass){
				//TODO better null assetNodeClass error
				throw new Error("you have not defined your assetNode classes for this project");
			}
			if(String(dataNode.@assetNode)){
				try {
					assetNodeClass = getDefinitionByName(String(dataNode.@assetNode)) as Class;
				} catch(error : Error){
					//TODO better invalid assetNodeClass error
					throw new Error("invalid asset node class: " + dataNode.@assetNode);
				}
			}
			assetNode = new assetNodeClass(pageNode);
			//define asset id
			if(String(dataNode.@id)) {
				assetNode.assetId = String(dataNode.@id);
			} else {
				//TODO better null asset id error
				throw new Error("You need to have an asset id in your site.xml!!!");
			}
			//define asset id
			if(String(dataNode.@src)) {
				assetNode.assetSrc = String(dataNode.@src);
			} else {
				//TODO better null asset src erros
				throw new Error("You need to have an asset src in your site.xml!!!");
			}
			//define asset loader class
			if(String(dataNode.@type)){
				assetLoaderClass = assetLoaderClassReference.getAssetLoaderClassFromType(String(dataNode.@type));
			} else {
				assetLoaderClass = assetLoaderClassReference.getAssetLoaderClassFromExtension(String(dataNode.@src));
			}
			assetNode.assetLoaderClass = assetLoaderClass;
			return assetNode;
		}

		//EVENT HANDELERS
		private function onXMLLoad(event : Event) : void
		{
			var loader : URLLoader = event.target as URLLoader;
			var siteXML : XML = new XML(loader.data);
			
			tree = siteTreeFromXML(siteXML);
			sendNotification(ErebusInitializationNotifications.INITIALIZE_PROXIES);
		}
		
		private function onXMLLoadError(event : IOErrorEvent) : void
		{
			//TODO better XML load error
			throw new Error("Can't load load site XML from URL: " + xmlSrc);
		}
	}
}
